package com.xishu.service.order.create;

import com.xishu.config.Constant;
import com.xishu.config.ContextConstant;
import com.xishu.entity.oplog.OrderOpLog;
import com.xishu.entity.order.Order;
import com.xishu.entity.shop.FoodTable;
import com.xishu.entity.shop.PayType;
import com.xishu.entity.shop.Shop;
import com.xishu.entity.shop.TakeOutSetting;
import com.xishu.response.ResponseStatus;
import com.xishu.response.VerifyUtil;
import com.xishu.service.*;
import com.xishu.service.order.base.OrderService;
import com.xishu.util.ServletUtil;
import com.xishu.util.Tools;
import org.apache.commons.chain.Command;
import org.apache.commons.chain.Context;
import org.apache.commons.chain.impl.ChainBase;
import org.apache.commons.chain.impl.ContextBase;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.Optional;

import static com.xishu.config.Constant.*;

public class CreateOrderChain extends ChainBase {
    private static Logger logger = LoggerFactory.getLogger(CreateOrderChain.class);

    public CreateOrderChain() {
        addCommand(new VerifyCmd());
        addCommand(new OrderNoCmd());
        addCommand(new DefaultValueCmd());
        addCommand(new TakeOutSettingCmd());
        addCommand(new TakeOutOrderCmd());
        addCommand(new QuickFoodCmd());
        addCommand(new FoodTableOrder());
        addCommand(new TakeOutPlatCmd());
    }

    /**
     * 创建订单
     *
     * @param order
     */
    public Order createOrder(Order order) throws Exception {
        CreateOrderChain chain = new CreateOrderChain();
        Context context = new ContextBase();
        context.put(ContextConstant.CONTEXT_ORDER, order);
        chain.execute(context);
        return order;
    }

}

class VerifyCmd implements Command, Tools {
    private static Logger logger = LoggerFactory.getLogger(VerifyCmd.class);

    @Override
    public boolean execute(Context context) throws Exception {
        Order order = (Order) context.get(ContextConstant.CONTEXT_ORDER);
        Shop shop = ShopService.getInstance().findShopById(order.getShopId());
        VerifyUtil.verify(() -> getBoolean(shop.getBusinessStatus()), ResponseStatus.SHOP_CLOSED);

        //如果不是B端的请求，那么不能有唯一码
        if (ServletUtil.isBEndPayOrder()) {
            logger.info("be end request");
            return false;
        } else {
            //C端请求，要么不传，要么传0
            VerifyUtil.verify(() -> order.getUniqueCode() == null || order.getUniqueCode() == 0, ResponseStatus.CONFIG_WRONG);

            //如果是C端扫描固定二维码的订单
            if (order.getUniqueCode() != null && order.getUniqueCode() == 0) {
                //如果是C端查询的时候生成的订单，那么defaultFoodTableOrder为true,setCustomerShow也为true
                VerifyUtil.verify(() -> getBoolean(order.getDefaultFoodTableOrder()) && getBoolean(order.getCustomerShow()), ResponseStatus.CONFIG_WRONG);
            }
        }

        return false;
    }
}

class OrderNoCmd implements Command {
    private static Logger logger = LoggerFactory.getLogger(OrderNoCmd.class);

    @Override
    public boolean execute(Context context) throws Exception {
        Order order = (Order) context.get(ContextConstant.CONTEXT_ORDER);
        Long shopId = order.getShopId();
        Integer orderNo = null;
        OrderService orderService = OrderService.getInstance();

        //B端操作的，一定需要生成餐桌码
        if (ServletUtil.getUserId() != null) {
            orderNo = orderService.createOrderNo(shopId);
            logger.info("create order no for b end user , order no {}", orderNo);
        } else if (OrderService.getInstance().isFoodTableOrder(order)) {
            orderNo = orderService.createOrderNo(shopId);
            logger.info("create order no for c end user and default food table , order no {}", orderNo);
        } else if (orderService.isTakeOutPlatOrder(order)) {
            orderNo = orderService.createOrderNo(shopId);
        }

        context.put(ContextConstant.CONTEXT_ORDER_NO, orderNo);
        return false;
    }
}

class DefaultValueCmd implements Command {
    private static Logger logger = LoggerFactory.getLogger(DefaultValueCmd.class);

    @Override
    public boolean execute(Context context) throws Exception {
        Order order = (Order) context.get(ContextConstant.CONTEXT_ORDER);

        OrderService orderService = OrderService.getInstance();
        Shop shop = ShopService.getInstance().findShopById(order.getShopId());
        //写餐厅的数据
        orderService.orderDefaultValue(order, shop);

        //判断是否是C端的订单
        Long userId = ServletUtil.getUserId();
        if (orderService.isTakeOutPlatOrder(order)) {
            order.setBusinessEndShow(true);
        } else {
            order.setBusinessEndShow(userId != null);
        }


        //B端创建的订单，那么直接确定落单时间
        if (userId != null) {
            order.setConfirmTime(System.currentTimeMillis());
        }

        return false;
    }
}

class TakeOutSettingCmd implements Command, Tools {
    private static Logger logger = LoggerFactory.getLogger(TakeOutSettingCmd.class);

    @Override
    public boolean execute(Context context) throws Exception {
        Order order = (Order) context.get(ContextConstant.CONTEXT_ORDER);
        Long shopId = order.getShopId();

        //如果传了takeoutsettingid，将值写到订单里面
        if (order.getTakeoutSettingId() != null) {
            TakeOutSetting takeOutSetting = TakeOutSettingService.getInstance().findById(order.getTakeoutSettingId());
            order.setTakeoutSetting_zh(takeOutSetting.getName_zh());
            order.setTakeoutSetting_en(takeOutSetting.getName_en());
            Long payTypeId = takeOutSetting.getPayTypeId();

            if (getBoolean(takeOutSetting.getEntity()) && payTypeId != null) {
                List<PayType> payTypeList = PayTypeService.getInstance().findPayTypeListCache(shopId);
                PayType payType = PayTypeService.getInstance().filterPayType(payTypeList, payTypeId);
                order.setTakeoutBindPayType(payType.getType());
                logger.info("will add pay type {} to order", payType.getType());
            }
        }

        return false;
    }
}


/**
 * 外卖订单
 */
class TakeOutOrderCmd implements Command, Tools {
    private static Logger logger = LoggerFactory.getLogger(TakeOutOrderCmd.class);

    @Override
    public boolean execute(Context context) throws Exception {
        Order order = (Order) context.get(ContextConstant.CONTEXT_ORDER);
        Integer orderNo = (Integer) context.get(ContextConstant.CONTEXT_ORDER_NO);
        OrderService orderService = OrderService.getInstance();
        Long shopId = order.getShopId();

        if (!orderService.isTakeoutOrder(order)) {
            logger.info("not the take out order");
            return false;
        }

        //需要生成取餐码
        logger.info("take out order");

        if (order.getOrderType() == null) {
            order.setOrderType(ORDER_TYPE_TAKE_OUT);
        }

        order.setOrderNo(orderNo);
        //添加线上付款，还是先付款的判断,只有C端的时候才需要判断
        //只有总二维码，餐桌二维码，服务员出单二维码单的时候，需要有判断
        orderService.addRunModelConfigForOrder(order);
        logger.info("end add run model config");

        //后付款模式，并且没有生成过订单号，这里生成一个
        if (!getBoolean(order.getPayFirst()) && order.getOrderNo() == null) {
            logger.info("will create new order no for pay after module");
            orderNo = orderService.createOrderNo(shopId);
            context.put(ContextConstant.CONTEXT_ORDER_NO, orderNo);
            order.setOrderNo(orderNo);
        }

        //快餐或者是外卖，生成取餐码
        if (order.getTakeOutNumber() == null) {
            logger.info("will add take out number {}", order.getOrderNo());
            order.setTakeOutNumber(order.getOrderNo());
        }

        if (orderService.isTakeOutPlatOrder(order)) {
            order.setStatus(Constant.ORDER_STATUS_END);
            order.setPayed(true);
            order.setReturnCash(false);
        } else {
            order.setStatus(Constant.ORDER_STATUS_STARTING);
        }

        CommonService.getInstance().createObject(order);

        OrderOpLogService orderOpLogService = OrderOpLogService.getInstance();
        //外卖创建订单，写入日志
        OrderOpLog orderOpLog = orderOpLogService.content(order, CREATE_ORDER);
        orderOpLogService.takeOutOrderLog(order, orderOpLog);
        //终止了
        return false;
    }
}

class QuickFoodCmd implements Command, Tools {
    private static Logger logger = LoggerFactory.getLogger(QuickFoodCmd.class);
    private OrderService orderService = OrderService.getInstance();

    @Override
    public boolean execute(Context context) throws Exception {
        Order order = (Order) context.get(ContextConstant.CONTEXT_ORDER);
        Integer orderNo = (Integer) context.get(ContextConstant.CONTEXT_ORDER_NO);

        if (getInt(order.getOrderType()) != ORDER_TYPE_QUICK_FOOD) {
            logger.info("not quick food order");
            return false;
        }

        order.setStatus(Constant.ORDER_STATUS_STARTING);
        order.setOrderNo(orderNo);

        //后付款模式，并且没有生成过订单号，这里生成一个
        if (!getBoolean(order.getPayFirst()) && order.getOrderNo() == null) {
            logger.info("will create new order no for pay after module");
            orderNo = orderService.createOrderNo(order.getShopId());
            context.put(ContextConstant.CONTEXT_ORDER_NO, orderNo);
            order.setOrderNo(orderNo);
        }

        //快餐或者是外卖，生成取餐码
        if (order.getTakeOutNumber() == null) {
            logger.info("will add take out number {}", order.getOrderNo());
            order.setTakeOutNumber(order.getOrderNo());
        }

        orderService.addRunModelConfigForOrder(order);
        logger.info("end add run model config");

        CommonService.getInstance().createObject(order);
        return false;
    }
}

class FoodTableOrder implements Command, Tools {
    private static Logger logger = LoggerFactory.getLogger(FoodTableOrder.class);

    @Override
    public boolean execute(Context context) throws Exception {
        Order order = (Order) context.get(ContextConstant.CONTEXT_ORDER);
        LockService localLockService = LockService.getInstance();
        OrderService orderService = OrderService.getInstance();
        CommonService commonService = CommonService.getInstance();

        if (!orderService.isFoodTableOrder(order)) {
            logger.info("not food table order");
            return false;
        }

        Long shopId = order.getShopId();

        StringBuilder sb = new StringBuilder();
        String lockKey = sb.append(order.getShopId()).append(SEPARATOR_KEY).append(order.getTableRow()).append(SEPARATOR_KEY).append(order.getTableColumn()).toString();

        Object lock = localLockService.getLock(lockKey);

        synchronized (lock) {
            logger.info("tang shi order, shopId is {} row {} column {} unicode {}", order.getShopId(), order.getTableRow(), order.getTableColumn(), order.getUniqueCode());
            VerifyUtil.verify(() -> !OrderService.getInstance().isExist(order), ResponseStatus.ORDER_EXIST);
            String foodTableKey = orderService.getFoodTableKey(order);
            order.setFoodTableKey(foodTableKey);
            order.setOrderType(Constant.ORDER_TYPE_TANG_SHI);
            order.setStatus(Constant.ORDER_STATUS_STARTING);

            //餐桌二维码订单
            if (getInt(order.getUniqueCode()) == 0) {
                order.setQrFoodTableOrder(true);
            }

            //通过uniqueCode去查询餐桌CODE
            FoodTableService foodTableService = FoodTableService.getInstance();

            if (getInt(order.getUniqueCode()) == 0) {
                logger.info("shop id {} row {} column {} scene id {}", order.getShopId(), order.getTableRow(), order.getTableColumn(), order.getSceneId());
                Optional<FoodTable> foodTableOptional = foodTableService.findTemplate(order.getShopId(), order.getTableRow(), order.getTableColumn(), order.getSceneId());
                VerifyUtil.verify(() -> foodTableOptional.isPresent(), ResponseStatus.SYSTEM_ERROR);
                FoodTable templateFoodTable = foodTableOptional.get();
                Integer showNo = foodTableService.createFoodTableNo(shopId, order.getTableRow(), order.getTableColumn(), order.getSceneId());
                FoodTable foodTable = FoodTableService.getInstance().createDefaultFoodTable(order.getShopId(), order.getTableRow(), order.getTableColumn(), order.getSceneId(), showNo, templateFoodTable.getCode());
                FoodTableService.getInstance().calTemplateStatusAndDiners(foodTable, templateFoodTable);
                //同步到其它餐桌
                foodTableService.syncCreateFoodTable(foodTable);

                //添加订单号
                order.setCode(foodTable.getCode());
                order.setShowNo(foodTable.getShowNo());
            } else {
                FoodTable foodTable = foodTableService.findFoodTableByUniqueCode(shopId, order.getTableRow(), order.getTableColumn(), order.getUniqueCode());
                logger.info("food table is {}", foodTable);
                order.setCode(foodTable.getCode());
                order.setShowNo(foodTable.getShowNo());
            }

            //添加线上付款，还是先付款的判断,只有C端的时候才需要判断
            //只有总二维码，餐桌二维码，服务员出单二维码单的时候，需要有判断
            orderService.addRunModelConfigForOrder(order);

            Integer orderNo = (Integer) context.get(ContextConstant.CONTEXT_ORDER_NO);

            order.setOrderNo(orderNo);

            //堂食，如果是后付款，那么需要添加订单号
            if (order.getOrderNo() == null && !getBoolean(order.getPayFirst())) {
                logger.info("will create new order no");
                orderNo = orderService.createOrderNo(shopId);
                order.setOrderNo(orderNo);
            }

            //如果是餐桌，并且是先付款，直接将付款方式改成一个人付款
            if (getInt(order.getOrderType()) == ORDER_TYPE_TANG_SHI && getBoolean(order.getPayFirst())) {
                logger.info("system set the order {} to one user pay", order.getOrderNo());
                order.setUserSelectPayType(USER_SELECT_PAY_TYPE_ONE_USER);
            }

            //不是先付款，生成取餐码
            if (!getBoolean(order.getPayFirst())) {
                orderService.dealTakeOutOrder(order);
            }

            commonService.createObject(order);
            //创建订单，写入日志
            OrderOpLogService instance = OrderOpLogService.getInstance();
            OrderOpLog content = instance.content(order, CREATE_ORDER);
            instance.createOrderLog(order, content);

            if (order.getShowNo() == null) {
                //开台写入日志
                OrderOpLogService opLogService = OrderOpLogService.getInstance();
                OrderOpLog foodTableContent = opLogService.content(order, OPEN_FOOD_TABLE);
                opLogService.openFoodTable(order, foodTableContent);
            }

            if (isNotEmpty(order.getShowNo())) {
                //搭台写入日志
                OrderOpLogService orderOpLogService = OrderOpLogService.getInstance();
                OrderOpLog takeFoodTableOpLog = orderOpLogService.content(order, TAKE_FOOD_TABLE);
                orderOpLogService.takeFoodTable(order, takeFoodTableOpLog);
            }


            //如果是固定餐桌，添加相关的订单ID
            FoodTable foodTable = foodTableService.findFoodTableByUniqueCode(order.getShopId(), order.getTableRow(), order.getTableColumn(), order.getUniqueCode());
            foodTable.setRelateOrderId(order.getId());
            commonService.save(foodTable);

            return true;
        }
    }
}

class TakeOutPlatCmd implements Command, Tools {
    private static Logger logger = LoggerFactory.getLogger(TakeOutPlatCmd.class);
    private OrderService orderService = OrderService.getInstance();

    private CommonService commonService = CommonService.getInstance();


    /**
     * 外卖平台订单，添加支付订单,并且直接支付完成
     *
     * @param context The {@link Context} to be processed by this
     *                {@link Command}
     * @return
     * @throws Exception
     */
    @Override
    public boolean execute(Context context) throws Exception {
        Order order = (Order) context.get(ContextConstant.CONTEXT_ORDER);

        if (!orderService.isTakeOutPlatOrder(order)) {
            logger.info("not the plat form order");
            return false;
        }

        Order payOrder = orderService.createPayOrder(order);
        payOrder.setUserCost(order.getUserCost());
        payOrder.setUserCost_b(order.getUserCost_b());
        payOrder.setLeftCash(0d);
        payOrder.setLeft_cash_b(0d);
        payOrder.setLeft_cash_c(0d);
        payOrder.setPayed(true);
        payOrder.setPayType(order.getPayType());
        payOrder.setPayTypeName_zh(order.getPayTypeName_zh());
        payOrder.setPayTypeName_en(order.getPayTypeName_en());
        payOrder.setStatus(ORDER_STATUS_END);

        commonService.createObject(payOrder);
        return false;
    }
}
